/*--------------------------------------------------------------------------------
* Copyright (c) 2022,西北农林科技大学信息学院计算机科学系
* All rights reserved.
*
* 文件名称：main.c
* 文件标识：见配置管理计划书
* 摘要：颠倒句子中单词，用指针数组管理的字符串数组实现。
*       首先使用遍历字符串，根据分隔符定位每个单词在原字符串中的起始地址。
*       然后构造指针数组管理字符串数组，记录每个单词。
*       最后反向指针数组，取得单词后实现结果拼接。
*
* 当前版本：1.0
* 作者：耿楠
* 完成日期：2022年11月26日
*
* 取代版本：无
* 原作者：耿楠
* 完成日期：
------------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 函数原型 */
size_t createWordSet(char *, const char *, char ***);
char* revSentenceWords(char *, const char *, const char *, const char *);
/* 判断是否为一个句子 */
int isSentence(const char *, const char *, const char *);

/* 错误响应 */
static void errormsg(const char *);

int main()
{
    /* 句子 */
    /* char s[] = "you can cage a swallow can't you?"; */
    char s[] = "   you can \n  cage a \tswallow  \ncan't you?   ";

    /* 原句子单词分隔符 */
    char delim[] = " \t\n\r";
    /* 句子终止符 */
    char term[] = ".?!";
    /* 结果句子单词分隔符 */
    char hyphen[] = " ";

    char *str = NULL;

    if(!isSentence(s, delim, term))
    {
        errormsg("Sentence needs a terminating character. (./?/!)");
    }

    str = revSentenceWords(s, delim, term, hyphen);
    puts(str);

    return 0;
}

/* 错误响应 */
static void errormsg(const char *message)
{
    printf("%s\n", message);
    exit(EXIT_FAILURE);
}

int isSentence(const char *s, const char *delim, const char *term)
{
    size_t len = 0;

    len = strlen(s);

    /* 删除尾部分隔符(空白) */
    len = strlen(s) - 1;
    while(strchr(delim, s[len]) && len >= 0)
    {
        len--;
    }

    /* 判断最后有没有句子终止字符 */
    if(!strchr(term, s[len]))
    {
        return 0;
    }

    return 1;
}

/*------------------------------------------------------------------------
// 名称：size_t createWordSet(char *s, const char *delim, char ***wordset)
// 功能：创建单词动态指针数组(字符串数组)。
// 算法：根据分隔符定位单词并将单词后的字符赋值为'\0'，以构成单词字符串，
//       同时，将该单词起始地址存入单词动态指针数组中。
// 参数：
//       [char* s] ------------- 待处理字符串的指针
//       [const char* delim] --- 单词单词分隔字符集指针
//       [char ***wordset] ----- 单词字符串数组(指针数组)的指针
// 返回：[size_t] --- 总计单词数
// 作者：耿楠
// 日期：2022年11月26日
------------------------------------------------------------------------*/
size_t createWordSet(char *s, const char *delim, char ***wordset)
{
    int cnt;

    char **w;
    size_t len;

    /* 假设有字符长度个单词(最大) */
    len = strlen(s);
    w = (char **)malloc(len * sizeof(char *));
    if(w == NULL)
    {
        errormsg("Not enough memory for word's set.");
    }
    memset(w, 0, len * sizeof(char*));

    /* 变量初值 */
    cnt = 0;
    w[cnt] = s;

    /* 扫描字符串直到终止字符 */
    while(*s)
    {
        /* 如果是分隔符，则移动当前单词指针和字符串指针到下一个字符*/
        /* 定位当前单词的起始位置*/
        if(strchr(delim, *s))
        {
            w[cnt]++;
            s++;

            continue;
        }

        /* 如果当前字符不是分隔符，下一个是分隔符但不是空字符*/
        if(!strchr(delim, *s) && (strchr(delim, *(s + 1)) && *(s + 1) != '\0'))
        {
            /* 将当前字符的下一个位置赋'\0'构成单词字符串*/
            *(s + 1) = '\0';

            /* 开始处理下一个单词 */
            s += 2;
            cnt++;
            w[cnt] = s;

            continue;
        }
        /* 常规字符，移动到下一个字符 */
        s++;
    }

    /* 重新分配内存，避免浪费空间 */
    /* 注意最后一个单词未参与计数 */
    w = (char **)realloc(w, (cnt + 1)  * sizeof(char*));
    if(w == NULL)
    {
        errormsg("Not enough memory for word's set realloc.");
    }

    /* 将单词信息动态数组首地址写回 */
    *wordset = w;

    /* 返回单词总数 */
    return cnt + 1;
}

/*------------------------------------------------------------------------
// 名称：char* revSentenceWords(char *s, const char *delim,
//                              const char *term, const char *hyphen)
// 功能：将句子按单词逆序(单词必须以空格分隔)。
// 算法：首先创建一个单词信息结构体动态数组，记录每个单词在字符串中的起始
//       地址和长度，然后遍历结构体动态数组在原字符串空间拼接结果字符串。
// 参数：
//       [char* s] -------------- 待逆序的字符串的指针
//       [const char* delim] ---- 单词单词分隔字符集指针
//       [const char* term] ----- 句子终止字符集指针
//       [const char* hyphen] --- 结果单词分隔字符集指针
// 返回：[char*] --- 逆序后字符串的指针
// 作者：耿楠
// 日期：2022年11月26日
// 注意：处理过程会对原字符串进行改写，如需要保留原串，请在调用该函数前备份。
------------------------------------------------------------------------*/
char* revSentenceWords(char *s, const char *delim,
                       const char *term, const char *hyphen)
{
    size_t words_cnt = 0;
    size_t str_len = strlen(s);

    char *str, *p;
    char **w = NULL;
    char terminator = 0;
    int i;

    str = (char*)malloc((str_len + 1) * sizeof(char));
    if(str == NULL)
    {
        errormsg("Not enough memory for reverse temp string.");
    }
    strcpy(str, s);

    /* 记录句子终止字符并删除原句子终止字符 */
    p = str + str_len - 1;
    while(!strchr(term, *p))
    {
        p--;
    }

    terminator = *p;
    *p = '\0';

    /* 创建单词集合，注意调用该函数前需要对字符首尾空白进行修剪 */
    words_cnt = createWordSet(str, delim, &w);

    /* 清空原串 */
    *s = '\0';

    /* 遍历单词数组(字符串数组，指针数组)，实现单词逆序后写入原字符串 */
    for(i = words_cnt - 1; i >= 0; i--)
    {
        strcat(s, w[i]);
        strcat(s, hyphen);
    }

    /* 处理句子终止符和字符串终止字符 */
    str_len = strlen(s);
    s[str_len - 1] = terminator;

    free(str);
    free(w);

    return s;
}
